package bll

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/url"
	"strings"
	"sync"
	"time"

	"gitee.com/sky_big/mylog"
	"github.com/gogf/gf/g/container/glist"
	"github.com/gogf/gf/g/container/gmap"
	"github.com/gogf/gf/g/net/ghttp"
	"github.com/gogf/gf/g/os/gcron"
	"github.com/gogf/gf/g/os/gtime"
	"github.com/gogf/gf/g/util/gconv"

	. "github.com/zhongdalu/DoorValveErWang/model"
	"github.com/zhongdalu/DoorValveErWang/public"
	. "github.com/zhongdalu/DoorValveErWang/record"
	"github.com/zhongdalu/DoorValveErWang/util"
)

var (
	once          *sync.Once
	biOnce        *sync.Once
	ciOnce        *sync.Once
	houseFac      *HouseModel
	snWdHisMap    *gmap.StrAnyMap
	warnModelChan = make(chan WarnModel)
)

const degree = 5

func init() {
	once = new(sync.Once)
	go warnWork()
}

// 初始阀开度设置
func InitSetup() error {
	go once.Do(WsWork)
	info, err := getApiData()
	if err != nil {
		return err
	}
	Close()
	data := info.Data
	Crd = NewCmdRecord()
	Crd.SetBalanceInterval(info.BalanceInterval)
	Crd.SetCheckInterval(info.CheckInterval)
	Crd.SetAllNum(len(data))
	Crd.RegisterRecordTimer(AddRecordTimer())
	go firstStep(data)
	return nil
}

func Close() {
	if Crd != nil {
		Crd.Reset()
		Crd = nil
		houseFac = nil
	}
}

func firstStep(data []LiveInfo) {
	Crd.SetStep(1)
	houseFac = NewHouseModel()
	Crd.GenClassId()
	for _, value := range data {
		houseId := value.HouseInfo
		houseInfo := NewHouseInfoModel(houseId)
		houseInfo.SetWarning(-1)
		houseFac.SetValue(houseInfo.id, houseInfo)
		fkd := int(getStdLl(value) / 9)
		sendCommand(NewCmdModel(value, fkd, Crd.GetClassId(), "初始设置阀开度"))
	}
	time.Sleep(time.Minute * 30)
	Crd.RegisterBiTimer(AddBITimer())
}

func warnWork() {
	for {
		select {
		case data := <-public.BackMsgChan:
			if data.IsComplete == true {
				if data.Errno != 0 {
					go func() {
						warnModelChan <- WarnModel{
							Type:     Crd.GetStep(),
							WarnType: public.WarnType5,
							HouseId:  data.HouseId,
							ClassId:  data.ClassId,
						}
					}()
				} else {
					house := houseFac.GetValue(data.HouseId)
					if house != nil {
						sendOperaResultInfo(OperaResultInfo{
							HouseInfo: house.id,
							ClassId:   data.ClassId,
							CurrFkd:   data.CurrFkd,
							NeedFkd:   data.NeedFkd,
							Jswd:      data.Jswd,
							Hswd:      data.Hswd,
							Snwd:      data.Snwd,
							Remarks:   data.Remarks,
							Error:     data.Error,
							Errno:     data.Errno,
						})
					}
				}
			}
		case model := <-warnModelChan:
			sendWarning(model)
		}
	}
}

func sendWarning(model WarnModel) {
	des := ""
	stdCount := 1
	switch model.WarnType {
	case public.WarnType0:
		des = "当前流量超出标准流量太多"
	case public.WarnType1:
		des = "阀开度已达最大值,回水温度还是达不到要求"
	case public.WarnType2:
		des = "阀开度已达最大值,流量低于标准流量过多"
	case public.WarnType3:
		des = "阀开度已达最大值,室内温度小于最低温度"
	case public.WarnType4:
		des = "回水温度总是达不到目标值"
		stdCount = 3
	case public.WarnType5:
		des = "设备无响应"
		stdCount = 3
	case public.WarnType6:
		des = "进回水温度一直达不到供热标准"
		stdCount = 3
	case public.WarnType7:
		des = "阀开度已达最小值，回水温度仍旧过高"
	default:
		mylog.Error("未知类型的报警")
		return
	}
	houseId := model.HouseId
	houseInfo := houseFac.GetValue(houseId)
	if houseInfo == nil {
		mylog.Error(fmt.Sprintf("id: %s 不存在", houseId))
		return
	}
	defer houseFac.SetValue(model.HouseId, houseInfo)
	// 记录次数
	houseInfo.AddWarnCount(model.WarnType)
	if houseInfo.GetWarnCount(model.WarnType) <= stdCount {
		log.Println(fmt.Sprintf("还未到报警次数:%+v", houseInfo), des)
		return
	}
	houseInfo.SetWarning(1)
	warnInfo := struct {
		ClassId   string `json:"uuid"`
		HouseInfo string `json:"house_info"`
		Type      string `json:"bjType"` // 1 2
		WarnType  int    `json:"warn_type"`
		Msg       string `json:"msg"`
	}{
		ClassId:   model.ClassId,
		HouseInfo: model.HouseId,
		Type:      gconv.String(model.Type),
		WarnType:  model.WarnType,
		Msg:       des,
	}
	bs, err := json.Marshal(warnInfo)
	if err != nil {
		mylog.Error(err)
		return
	}
	var c = ghttp.NewClient()
	c.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	dataUrlVal := url.Values{}
	dataUrlVal.Add("passStr", string(bs))
	resp, err := c.Post(public.HttpUrl+"/hf_interface.ashx?method=addBj", dataUrlVal.Encode())
	if err != nil {
		mylog.Error(err)
		return
	}
	mylog.Println("?method=addBj=>", resp.ReadAllString(), fmt.Sprintf("%+v", dataUrlVal))
	defer func() {
		_ = resp.Close()
	}()
	if resp.StatusCode != 200 {
		mylog.Error(resp.Status)
	}
}

func AddRecordTimer() *gcron.Entry {
	fmt.Println("开始记录")
	gr := gcron.New()
	entry, err := gr.Add(fmt.Sprintf("@every %dm", 3), func() {
		err := records(Crd)
		if err != nil {
			mylog.Error(err)
		}
	})
	if err != nil {
		mylog.Error(err)
		return nil
	}
	return entry
}

func AddBITimer() *gcron.Entry {
	mylog.Println("开启第二阶段")
	biOnce = new(sync.Once)
	gr := gcron.New()
	entry, err := gr.Add(fmt.Sprintf("@every %dh", Crd.GetBalanceInterval()), transferBi)
	if err != nil {
		mylog.Error(err)
		return nil
	}
	return entry
}

func AddCITimer() *gcron.Entry {
	mylog.Println("开启第三阶段")
	ciOnce = new(sync.Once)
	snWdHisMap = gmap.NewStrAnyMap()
	gr := gcron.New()
	entry, err := gr.Add(fmt.Sprintf("@every %dh", Crd.GetCheckInterval()), transferCi)
	if err != nil {
		mylog.Error(err)
		return nil
	}
	return entry
}

func transferCi() {
	info, err := getApiData()
	if err != nil {
		return
	}
	Crd.GenClassId()
	ciOnce.Do(func() {
		Crd.SetStep(3)
		if houseFac != nil {
			houseFac.ResetComplete()
		}
	})
	overTemp := info.OverTemp
	subTemp := info.SubTemp
	for _, value := range info.Data {
		houseId := value.HouseInfo
		houseFac.SetIfNotExist(houseId, NewHouseInfoModel(houseId))
		houseInfo := houseFac.GetValue(houseId)
		if houseInfo.GetWarning() != 0 {
			fmt.Println("第三阶段 不符合判断条件 ", houseInfo)
			continue
		}
		if snWdHisMap.Contains(houseId) {
			if list, ok := snWdHisMap.Get(houseId).(glist.List); ok {
				if h, ok := list.PopFront().(float64); ok {
					if math.Abs(h-value.Snwd) > value.SnwdRange {
						list.PushFront(value.Snwd)
						snWdHisMap.Set(houseId, list)
						continue
					}
				}
			}
		} else {
			list := glist.New()
			list.PushFront(value.Snwd)
			snWdHisMap.Set(houseId, list)
			continue
		}
		equip := value.EquipInfo
		fkd := equip.Fkd
		if value.Snwd > overTemp {
			// 适当减少阀开度
			fkd -= degree
			if fkd < 0 {
				fkd = 0
			}
			sendCommand(NewCmdModel(value, fkd, Crd.GetClassId(), "室内温度大于最高值"))
		} else if value.Snwd < subTemp {
			// 小于最低温度并且阀开度达到最大时，报警
			if fkd > 100-degree {
				warnModelChan <- WarnModel{
					Type:     Crd.GetStep(),
					WarnType: public.WarnType3,
					HouseId:  value.HouseInfo,
					ClassId:  Crd.GetClassId(),
				}
			} else {
				fkd += degree
				sendCommand(NewCmdModel(value, fkd, Crd.GetClassId(), "室内温度低于最小值"))
			}
		} else {
			// 符合标准
			houseInfo.SetWarning(-1)
			houseFac.SetValue(houseInfo.id, houseInfo)
		}
	}
	// houseFac.Wait()
	// 关闭平衡
	if houseFac.IsComplete() == true {
		Crd.SetStep(4)
		err := records(Crd)
		if err != nil {
			mylog.Error(err)
		}
		mylog.Println("平衡结束")
		Crd.CancelAllTimer()
	}
}

// 获取web接口的数据 判断是否需要调平衡 需要的话 发送命令给命令交互
func transferBi() {
	info, err := getApiData()
	if err != nil {
		return
	}
	biOnce.Do(func() {
		houseFac = NewHouseModel()
		Crd.SetStep(2)
	})
	Crd.GenClassId()
	for _, value := range info.Data {
		houseFac.SetIfNotExist(value.HouseInfo, NewHouseInfoModel(value.HouseInfo))
		houseInfo := houseFac.GetValue(value.HouseInfo)
		if houseInfo.GetWarning() != 0 {
			fmt.Println("第二阶段 不符合判断条件 ", houseInfo)
			continue
		}
		hswd := value.HsHisTemp
		jswd := value.JsHisTemp
		// 进回水温度是否达到供热标准
		if hswd > value.HsWdStd && jswd > value.JsWdStd {
			equip := value.EquipInfo
			fkd := equip.Fkd
			ls := equip.Ls
			ll := ls * 1000
			if ll > getStdLl(value)+200 {
				// 超出标准流量太多 报警
				warnModelChan <- WarnModel{
					Type:     Crd.GetStep(),
					WarnType: public.WarnType0,
					HouseId:  value.HouseInfo,
					ClassId:  Crd.GetClassId(),
				}
				continue
			}
			if hswd-value.TargetTemp > 2 {
				if fkd > degree {
					fkd -= degree
					sendCommand(NewCmdModel(value, fkd, Crd.GetClassId(), "回水温度大于标准值"))
				} else {
					warnModelChan <- WarnModel{
						Type:     Crd.GetStep(),
						WarnType: public.WarnType7,
						HouseId:  value.HouseInfo,
						ClassId:  Crd.GetClassId(),
					}
				}
			} else if hswd-value.TargetTemp < (-2) {
				if fkd < 100-degree {
					fkd += degree
					sendCommand(NewCmdModel(value, fkd, Crd.GetClassId(), "回水温度小于标准值"))
				} else {
					wTp := public.WarnType2
					if ll < getStdLl(value)-200 {
						// 流速未达到报警
						wTp = public.WarnType2
					} else {
						wTp = public.WarnType4
					}
					warnModelChan <- WarnModel{
						Type:     Crd.GetStep(),
						WarnType: wTp,
						HouseId:  value.HouseInfo,
						ClassId:  Crd.GetClassId(),
					}
				}
			} else {
				// 正常
				houseInfo.SetWarning(-1)
				houseFac.SetValue(houseInfo.id, houseInfo)
			}
		} else {
			warnModelChan <- WarnModel{
				Type:     Crd.GetStep(),
				WarnType: public.WarnType6,
				HouseId:  value.HouseInfo,
				ClassId:  Crd.GetClassId(),
			}
		}
	}
	// houseFac.Wait()
	if houseFac.IsComplete() == true {
		Crd.CancelBiTimer()
		Crd.RegisterCiTimer(AddCITimer())
	}
}

/*
	 命令
	 "01",    //"温控工作模式，阀门开度值",
	 "02",    //"进回水平均温度调节",
	 "04",    //"回水温度调节",

	1.手动模式 2 进回水温差  4 回水温度
*/
func sendCommand(model *CmdModel) {
	arr := []string{"100", model.MpNo}
	switch model.Mod {
	case 0:
		arr = append(arr, "01")
	default:
		mylog.Error("当前设备模式不适合自动平衡:", model.Mod)
		return
	}
	arr = append(arr, gconv.String(model.Value))
	v := strings.Join(arr, ",")
	v += ",0,0"
	cmd := Command{
		Data: []CommandItem{
			{
				Cjq:   model.Center,
				Dtu:   model.Dtu,
				Value: []string{v},
			},
		},
		Dt:       gtime.Datetime(),
		Fac:      completeFac(model.Fac, "_1_1"),
		OrderID:  10074,
		Priority: 3,
		Recopy:   3,
		Timeout:  50,
		UUID:     util.Rand().Hex(),
	}
	SendCmdChan <- SendCmdMode{
		Command: cmd,
		AddInfo: model.AddInfo,
	}
}

func completeFac(fac, ext string) string {
	if strings.Contains(fac, ext) {
		return fac
	}
	return fac + ext
}
